package net.barashev.seclub.io.file;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.HashMap;
import java.util.Map;

import net.barashev.seclub.io.DataMarshaller;

public class DiskFileManager implements FileManager {
    private static class DiskFile<T> implements VFile<T> {
        private File file;
        private DataMarshaller<T> marshaller;

        DiskFile(File file, DataMarshaller<T> marshaller) {
            this.file = file;
            this.marshaller = marshaller;
        }

        public byte[] getContent() throws IOException {
            FileInputStream fistream = new FileInputStream(this.file);
            byte[] buffer = new byte[(int) this.file.length()];
            fistream.read(buffer);
            fistream.close();
            return buffer;
        }

		public long getLengthInBytes() {
			return this.file.length();
		}

		@Override
        public FileChannel getFileChannel() {
            try {
                return new RandomAccessFile(this.file, "rw").getChannel();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }

        public DataMarshaller<T> getMarshaller() {
            return this.marshaller;
        }

        public String getName() {
            return this.file.getName();
        }

        public ReadableByteChannel getReadableChannel() {
            try {
                return new FileInputStream(this.file).getChannel();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }

        public WritableByteChannel getWritableChannel() {
            try {
                return new FileOutputStream(this.file).getChannel();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }
    }

    private final File root;
    private final Map<String, DiskFile> name2file = new HashMap<String, DiskFile>();

    public DiskFileManager(String directory) {
        this.root = new File(directory);
        if (!this.root.exists() || !this.root.isDirectory() || !this.root.canWrite()) {
            throw new RuntimeException("Can't use directory=" + directory + ", please check that it does exist and is writable");
        }

    }
    public <T> VFile<T> getFile(String name) {
        return this.name2file.get(name);
    }

    public <T> VFile<T> createFile(String name, DataMarshaller<T> marshaller) {
        if (this.name2file.containsKey(name)) {
            throw new IllegalArgumentException("File "+name+" already exists");
        }
        DiskFile<T> result = new DiskFile<T>(new File(this.root, name), marshaller);
        this.name2file.put(name, result);
        return result;
    }
    public <T> VFile<T> createFile(String name, DataMarshaller<T> marshaller, byte[] bytes) throws IOException {
        DiskFile<T> result = (DiskFile<T>) createFile(name, marshaller);
        FileOutputStream fostream = new FileOutputStream(result.file);
        fostream.write(bytes);
        fostream.flush();
        fostream.close();
        return result;
    }
    public File getRoot() {
        return this.root;
    }
    public Iterable<String> getFileNames() {
        return name2file.keySet();
    }
}
